<?php
/**
 * clase_zen_POA_AnalizadorSintactico.php
 * @author Juan Belon
 * @access public
 * @copyright LGPL, GPL
 * @package zen_POA
 * @version 0.1.1
 * @uses zenphp FrameWork
 * @link http://csl2-zenphp.forja.rediris.es
 * @link http://www.zenphp.es
 * @link https://forja.rediris.es/projects/csl2-zenphp/
 * @magic Analiza Sintácticamente un programa que fue insertado en una cadena XML
 * TODO: Completar la Documentación de las clases de POA
 */
// +----------------------------------------------------------------------
// | zenphp.es
// +----------------------------------------------------------------------
/**
* @desc 
* @see http://www.php.net/manual/es/ref.tokenizer.php
* Codigo formateado con PolyStyle demo
*/
class zen_POA_AnalizadorSintactico
    {
    /**
    * @desc 
    * @var str
    */
    var $inicio;
    /**
    * @desc 
    * @var str
    */
    var $tamanio;
    /**
    * @desc 
    * @var str
    */
    var $codigo;
    /**
    * @desc Para usar token_get_all()
    * @var array
    */
    var $tokens;
    /**
    * @desc Indices para los arrays
    * @var str
    */
    var $indice;
    /**
    * @desc Tokens no válidos
    * @var array
    */
    var $tokensInvalidos;
    /**
    * @desc Tokens válidos
    * @var array
    */
    var $tokensValidos;
    /**
    * @desc Constructor del analizador sintáctico para programación orientada a aspectos
    * @param str $cadena
    * @return zen_POA_AnalizadorSintactico
    */
    function zen_POA_AnalizadorSintactico($cadena)
        {
        $this->codigo =(trim(substr($cadena, 0, 2)) == "<?") ? $cadena : "<?php " . $cadena . " ?>";

        // Referencia: http://bugs.php.net/bug.php?id=28391
        $this->tokens =token_get_all($this->codigo);
        //Para considerar los casos en que nos devuelva un resultado no esperado de elementos leidos con < ? ...
        //Quitar los comentarios para comprobar que hace nuestro Generador de Tokens en la versión de PHP que tenemos...
        //echo "<pre>" . print_r($this->tokens, true) . "</pre>";


        // inicioial ETIQUETA_APERTURA
        $this->inicio =0; // 0: consideremos que PHP Tokenizer quita las etiquetas de apertura de PHP

        // inicioial ETIQUETA_CLAUSURA
        $this->tamanio=count($this->tokens) - 1; // count() - 1: posiblemente sea una etiqueta de clausura de PHP

        if (count($this->tokens) > 0)
            {
            // Arreglo:  ETIQUETA_APERTURA
            if (!is_array($this->tokens[0]) && $this->tokens[0] == "<")
                {
                // 3: PHP : Etiqueta abierta - 0 => <, 1 => ?, 2 => php
                $this->inicio=3;
                }
            elseif (is_array($this->tokens[0]) && token_name($this->tokens[0][0]) == "T_OPEN_TAG")
                {
                // 1: PHP Etiqueta abierta - 0 => < ? php
                $this->inicio=1;
                }

            // Arreglo:  ETIQUETA_CLAUSURA
            if (is_array(
                    $this->tokens[$this->tamanio]) && token_name($this->tokens[$this->tamanio][0]) != "T_CLOSE_TAG")
                {
                $this->tamanio++; // incrementarar en uno (asi como: $l = count($tok);)
                }
            }

        $this->indice=$this->inicio;

        // Inicialmente la lista de Tokens inválidos está vacía
        $this->tokensInvalidos=array();

        // Lo mismo para la lista de válidos
        $this->tokensValidos=array();
        }

    /**
    * @desc Obtiene un token de la lista con indice $i
    * @return 
    */
    function obtenerToken($i)
        {
        if ($i >= 0 && $i < $this->tamanio)
            {
            return $this->tokens[$i];
            }

        return null;
        }

    /**
    * @desc 
    */
    function obtenerNombreToken($i)
        {
        if ($i >= 0 && $i < $this->tamanio)
            {
            $token=$this->tokens[$this->indice];
            return (is_array($token) ? $token[1] : $token);
            }

        return null;
        }

    /**
    * @desc 
    */
    function ponerTokensValidos($tokensValidos = array())
        {
        if (!is_array($tokensValidos))
            {
            $tokensValidos=array($tokensValidos);
            }

        $this->tokensValidos=$tokensValidos;
        }

    /**
    * @desc 
    */
    function obtenerTokensValidos() { return $this->tokensValidos; }

    /**
    * @desc 
    */
    function ponerTokensInvalidos($tokensInvalidos = array())
        {
        if (!is_array($tokensInvalidos))
            {
            $tokensInvalidos=array($tokensInvalidos);
            }

        $this->tokensInvalidos=$tokensInvalidos;
        }

    /**
    * @desc 
    */
    function obtenerTokensInvalidos() { return $this->tokensInvalidos; }

    /**
    * @desc 
    */
    function ponerIndice($i) {
    // El indice es para saltar en la pila de Tokens
    $this->indice=$i; }

    /**
    * @desc 
    */
    function obtenerIndice() { return $this->indice; }

    /**
    * @desc 
    */
    function obtenerInicio() { return $this->inicio; }

    /**
    * @desc 
    */
    function obtenerTamanio() { return $this->tamanio; }
    /**
    * @desc 
    */
    function obtenerCodigo() { return $this->codigo; }
    /**
    * @desc 
    */
    function tokenActual() { return $this->siguienteToken(false); }
    /**
    * @desc 
    */
    function nombreTokenActual() { return $this->siguienteNombreToken(false); }
    /**
    * @desc 
    */
    function siguienteToken($incrementar = true)
        {
        if ($incrementar)
            {
            $this->incrementar();
            }

        return $this->obtenerToken($this->indice);
        }
    /**
    * @desc 
    */
    function siguienteNombreToken($incrementar = true)
        {
        if ($incrementar)
            {
            $this->incrementar();
            }

        return $this->obtenerNombreToken($this->indice);
        }
    /**
    * @desc 
    */
    function anteriorToken($decrementar = true)
        {
        if ($decrementar)
            {
            $this->decrementar();
            }

        return $this->obtenerToken($this->indice);
        }
    /**
    * @desc 
    */
    function anteriorNombreToken($decrementar = true)
        {
        if ($decrementar)
            {
            $this->decrementar();
            }

        return $this->obtenerNombreToken($this->indice);
        }
    /**
    * @desc 
    */
    function reiniciar() { $this->indice=$this->inicio; }
    /**
    * @desc 
    */
    function incrementar()
        {
        do
            {
            $this->indice++;

            // PHP 4 Prevenir error del analizador:
            if (array_key_exists($this->indice, $this->tokens))
                {
                $token=$this->tokens[$this->indice];
                }
            else
                {
                $this->indice=$this->tamanio;
                }
            } while ($this->indice < $this->tamanio && ($this->esTokenInvalido(
                                                            $token) || !$this->esTokenValido($token)));
        }
    /**
    * @desc 
    */
    function decrementar()
        {
        do
            {
            $this->indice--;

            // PHP 4:: Evita el error del analizador
            if (array_key_exists($this->indice, $this->tokens))
                {
                $token=$this->tokens[$this->indice];
                }
            else
                {
                $this->indice=-1;
                }
            } while ($this->indice >= 0 && ($this->esTokenInvalido($token) || !$this->esTokenValido($token)));
        }
    /**
    * @desc 
    */
    function esTokenInvalido($token)
        {
        $tipo_token=(is_array($token)) ? token_name((int)$token[0]) : $token;

        if (count($this->tokensInvalidos) < 1)
            {
            return false;
            }

        $resultado=array_search($tipo_token, $this->tokensInvalidos);

        return ($resultado !== null && $resultado !== false);
        }
    /**
    * @desc 
    */
    function esTokenValido($token)
        {
        $tipo_token=(is_array($token)) ? token_name((int)$token[0]) : $token;

        if (count($this->tokensValidos) < 1)
            {
            return true;
            }

        $resultado=array_search($tipo_token, $this->tokensValidos);

        return ($resultado !== null && $resultado !== false);
        }
    }
?>